﻿using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using MyFramework.Common;
using MyFramework.Common.Enums;
using MyFramework.Common.Interfaces;

namespace MyFramework.Core
{
    /// <summary>
    /// view collection
    /// </summary>
    public sealed class ViewCollection : ObservableCollection<IView>,IViewCollection
    {
        #region Fields

        /// <summary>
        /// The owner view model
        /// </summary>
        private IViewModel ownerViewModel;

        /// <summary>
        /// The removing
        /// </summary>
        [ThreadStatic]
        private bool removing;

        /// <summary>
        /// The main view
        /// </summary>
        private IView mainView;

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="ViewCollection"/> class.
        /// </summary>
        /// <param name="owner">The owner.</param>
        public ViewCollection(IViewModel owner)
        {
            this.ownerViewModel = owner;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the main view.
        /// </summary>
        /// <value>
        /// The main view.
        /// </value>
        public IView MainView
        {
            get { return this.mainView; }
            private set { if (this.mainView != value) { this.mainView = value; this.OnPropertyChanged(new PropertyChangedEventArgs("MainView")); } }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Adds the main view.
        /// </summary>
        /// <param name="view">The view.</param>
        /// <exception cref="System.InvalidOperationException"></exception>
        public void AddMainView(IView view)
        {
            if (this.MainView == null)
            {
                this.Add(view);
                this.MainView = view;
            }
            else
            {
                throw new InvalidOperationException(MyFramework.Core.Resources.MainViewAlreadyAddedToCollection);
            }
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources.
        /// </summary>
        /// <typeparam name="TView">The type of the view.</typeparam>
        public void Dispose<TView>() where TView : IView
        {
            var view = this.OfType<TView>().FirstOrDefault();
            if (view != null)
            {
                view.Dispose();
            }
        }

        /// <summary>
        /// Inserts the item.
        /// </summary>
        /// <param name="index">The index.</param>
        /// <param name="item">The item.</param>
        protected override void InsertItem(int index, IView item)
        {
            base.InsertItem(index, item);

            if (item.State < ViewState.Initialized)
            {
                item.Initialize(this.ownerViewModel);
            }
        }

        /// <summary>
        /// Sets the item.
        /// </summary>
        /// <param name="index">The index.</param>
        /// <param name="item">The item.</param>
        protected override void SetItem(int index, IView item)
        {
            base.SetItem(index, item);

            if (item.State < ViewState.Initialized)
            {
                item.Initialize(this.ownerViewModel);
            }
        }

        /// <summary>
        /// Clears the items.
        /// </summary>
        protected override void ClearItems()
        {
            while (this.Count > 0)
            {
                this.RemoveItem(0);
            }
        }

        /// <summary>
        /// Removes the item.
        /// </summary>
        /// <param name="index">The index.</param>
        protected override void RemoveItem(int index)
        {
            try
            {
                this.removing = true;

                var item = base[index];
                if (item.State < ViewState.Disposing)
                {
                    item.Dispose();
                }
                base.RemoveItem(index);
            }
            finally
            {
                this.removing = false;
            }
        }

        /// <summary>
        /// Raises the <see cref="E:CollectionChanged" /> event.
        /// </summary>
        /// <param name="e">The <see cref="NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            base.OnCollectionChanged(e);

            if (e.NewItems != null)
            {
                foreach (var item in e.NewItems)
                {
                    var npc = item as INotifyPropertyChanged;
                    if (npc != null)
                    {
                        npc.PropertyChanged += OnItemPropertyChanged;
                    }
                }
            }

            if (e.OldItems != null)
            {
                foreach (var item in e.OldItems)
                {
                    var npc = item as INotifyPropertyChanged;
                    if (npc != null)
                    {
                        npc.PropertyChanged -= OnItemPropertyChanged;
                    }
                }
            }
        }

        /// <summary>
        /// Called when [item property changed].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="PropertyChangedEventArgs"/> instance containing the event data.</param>
        private void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "State")
            {
                var item = sender as IView;
                if (item.State == ViewState.Disposed && !removing)
                {
                    this.Remove(item);
                }
            }
        }

        #endregion
    }
}
